﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace ReactiveCode.Observables
{
    class LatestValue<T> : IObservable<T>
    {
        IObservable<T> _source;
        bool _distinct;
        public T Value { get; private set; }
        public bool HasValue { get; private set; }

        Subject<T> _listeners;
        IDisposable _subscription;

        public bool Enabled
        {
            get { return _subscription != null; }
            set
            {
                if (value == Enabled)
                    return;

                if (value)
                {
                    _subscription = _source.Subscribe(x =>
                    {
                        bool hadValue = HasValue;
                        T oldValue = Value;

                        Value = x;
                        HasValue = true;

                        if (_listeners != null)
                        {
                            if (_distinct && hadValue && EqualityComparer<T>.Default.Equals(oldValue, Value))
                                return;
                            _listeners.OnNext(x);
                        }
                    });
                }
                else
                {
                    _subscription.Dispose();
                    _subscription = null;
                }
            }
        }

        public LatestValue(IObservable<T> source, bool distinct = false)
        {
            Contract.Requires(source != null);
            _distinct = distinct;
            _source = source;
            Enabled = true;
        }
        public void Dispose()
        {
            if (_listeners != null)
                _listeners.OnCompleted();
            _subscription.Dispose();
        }
        public IDisposable Subscribe(IObserver<T> observer)
        {
            if (HasValue)
                observer.OnNext(Value);

            if (_listeners == null)
                _listeners = new Subject<T>();

            return _listeners.Subscribe(observer);
        }

        public override string ToString()
        {
            if (!HasValue)
                return "<no value>";
            return "{ " + Value + " }";
        }
    }
}
